package com.huitone.smspfm.socket.server;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.huitone.smspfm.socket.core.AbstractMessageHandler;
import com.huitone.smspfm.socket.core.SocketHolder;


/**   
* @Title SocketHolderManager.java 
* @Package com.huitone.smspfm.socket.server 
* @Description TODO(用一句话描述该文件做什么) 
* @author ybliang   
* @date 2017年3月27日 下午4:35:20 
* @version V1.0   
*/
public class SocketHolderManager {
	
	private static final Logger logger = LogManager.getLogger(SocketHolderManager.class);
	
	private Map<String, List<SocketHolder>> store = new ConcurrentHashMap<String, List<SocketHolder>>();
	
	int maxActive = 15;//StringUtils.toInteger(StringUtils.replaceWhenNull(ContextPropertiesUtils.getProperty("smspfm.server.maxActive"), "30"), 30, true);
	
	long freeTimeMillis = 10000;//StringUtils.toInteger(StringUtils.replaceWhenNull(ContextPropertiesUtils.getProperty("smspfm.server.maxTimeRelease"), "10000"), 1000, true);
	
	// 最大空闲时间
	private long maxFreeTimeMillinsToClear = 30000;
	
	// 守护狗的执行时间间隔
	private long watchDogDelay = 5000;
	
	// 同一个客户端可同时打开的连接数
	private int maxSameSrvNodeCount = 5;
	
	private SocketHolderWatchDog socketHolderWatchDog = null;
	
	private static SocketHolderManager instance = new SocketHolderManager();
	public static SocketHolderManager getInstance() {
		return instance;
	}
	
	private SocketHolderManager(){}
	
	public synchronized BingResult bind(SocketHolder socketHolder) {
		
		if (socketHolderWatchDog == null) {
			socketHolderWatchDog = new SocketHolderWatchDog(this).setDelay(watchDogDelay);
			socketHolderWatchDog.start();
		}
		
		BingResult result = BingResult.SUCCESS;
		
		if (socketHolder != null && socketHolder.getAccountInfo().isLogin()) {
			
			// 找出超时的连接移除
			removeOutTimes();
			
			String key = socketHolder.setForServer(true).getAccountInfo().getSrvNode();
			
			if (store.containsKey(key)) {
				
				if (this.getCurrentActiveNode() < maxActive) {
					if (store.get(key).size() < maxSameSrvNodeCount) {
						store.get(key).add(socketHolder);
						result = BingResult.SUCCESS;
					} else {
						result = BingResult.MXSRVNODE;
					}
				} else {
					result = BingResult.MXFAILED;
				}
			} else {
				if (this.getCurrentActiveNode() < maxActive) {
					List<SocketHolder> list = new ArrayList<SocketHolder>();
					list.add(socketHolder);
					store.put(key, list);
					result = BingResult.SUCCESS;
				} else {
					result = BingResult.MXFAILED;
				}
			}
		} else {
			result = BingResult.ADDFAILED;
		}
		
		if (result == BingResult.SUCCESS) {
			logger.info("新建客户端:" + socketHolder.getAccountInfo().getSrvNode() + "连接");
		}
		
		return result;
	}
	
	public synchronized void unbind(SocketHolder socketHolder) {
		if (socketHolder != null) {
			String key = socketHolder.getAccountInfo().getSrvNode();
			if (store.containsKey(key)) {
				List<SocketHolder> list = store.get(key);
				if (list.contains(socketHolder)) {
					socketHolder.close();
					boolean b = list.remove(socketHolder);
					if (b) {
						logger.info("移除客户端:" + socketHolder.getAccountInfo().getSrvNode() + "连接");
					}
				}
				if (list.isEmpty()) {
					store.remove(key);
				}
			}
		}
	}
	
	public synchronized void removeOutTimes() {
		
		int activeSrvNode = 0;
		
		Iterator<Entry<String, List<SocketHolder>>> iterator = store.entrySet().iterator();
		
		while(iterator.hasNext()) {
			
			Entry<String, List<SocketHolder>> entry = iterator.next();
			
			String key = entry.getKey();
			List<SocketHolder> list = entry.getValue();
			
			Iterator<SocketHolder> ite = list.iterator();
			while(ite.hasNext()) {
				SocketHolder socketHolder = ite.next();
				logger.debug("超时时间:" + socketHolder.getFreeTimeMillis());
				if (socketHolder.getFreeTimeMillis() >= maxFreeTimeMillinsToClear) {
					socketHolder.close();
					logger.warn("移除:" + socketHolder.getAccountInfo().getSrvNode() + " 超时时间:" + socketHolder.getFreeTimeMillis());
					ite.remove();
				}
			}
			
			activeSrvNode += list.size();
			
			if (list.isEmpty()) {
				store.remove(key);
			}
		}
		
		logger.debug("当前活动的客户端数:" + activeSrvNode);
	}
	
	/**
	 * 获取当前保持中的连接数
	 * @return
	 */
	public int getCurrentActiveNode() {
		
		int count = 0;
		
		Iterator<Entry<String, List<SocketHolder>>> iterator = store.entrySet().iterator();
		
		while (iterator.hasNext()) {
			Entry<String, List<SocketHolder>> entry = iterator.next();
			List<SocketHolder> list = entry.getValue();
			if (list != null) {
				count += list.size();
			}
		}
		
		return count;
	}
	
	class SocketHolderWatchDog extends Thread {
		
		private long delay = 60000;
		
		private SocketHolderManager socketHolderManager = null;
		
		public SocketHolderWatchDog(SocketHolderManager socketHolderManager) {
			this.socketHolderManager = socketHolderManager;
		}
		
		@Override
		public void run() {
			
			int countToExcute = (int) (delay / 1000);
			int count = 0;
			
			while (!this.isInterrupted()) {
				
				if (count >= countToExcute) {
					if (socketHolderManager != null) {
						socketHolderManager.removeOutTimes();
					}
					count = 0;
				} else {
					count ++;
				}
				
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					break;
				}
			}
		}

		public SocketHolderWatchDog setDelay(long delay) {
			this.delay = delay;
			return this;
		}
	}

	public Integer getMaxActive() {
		return maxActive;
	}

	public void setMaxActive(Integer maxActive) {
		this.maxActive = maxActive;
	}

	public int getMaxSameSrvNodeCount() {
		return maxSameSrvNodeCount;
	}

	public void setMaxSameSrvNodeCount(int maxSameSrvNodeCount) {
		this.maxSameSrvNodeCount = maxSameSrvNodeCount;
	}
	
	public synchronized void release() {
		
		Iterator<Entry<String, List<SocketHolder>>> iterator = store.entrySet().iterator();
		
		while(iterator.hasNext()) {
			Entry<String, List<SocketHolder>> next = iterator.next();
			List<SocketHolder> list = next.getValue();
			if (list != null) {
				for (SocketHolder sh:list) {
					sh.close();
					AbstractMessageHandler messageHandler = sh.getMessageHandler();
					if (messageHandler != null) {
						messageHandler.shutdown();
					}
				}
			}
		}
	}

	public void shutdown() {
		
		if (socketHolderWatchDog != null) {
			socketHolderWatchDog.interrupt();
		}
		
		release();
	}
}
